package org.jugile.jsontmpl.domain;

import java.util.ArrayList;
import java.util.Map;

import org.jugile.jsontmpl.common.*;
import org.jugile.jsontmpl.domain.ValueBase;
import org.jugile.util.Jugile;
import org.jugile.util.Node;
import org.jugile.util.Proxy;
import org.jugile.util.Time;
import org.jugile.util.Vo;

public class Value extends ValueBase implements org.jugile.util.IDO {

	
	public Node toXml() {
		FldType ft = getType();
		if (FldType.STR == ft) {
			return new Node(FldType.STR.getLabel(), getValue());
		}
		if (FldType.NUM == ft) {
			return new Node(FldType.NUM.getLabel(), getValue());
		}
		if (FldType.BOOL == ft) {
			return new Node(FldType.BOOL.getLabel(), getValue());
		}
		if (FldType.TS == ft) {
			return new Node(FldType.TS.getLabel(), getValue());
		}
		if (FldType.OBJ == ft) {
			return getObj().toXml();
		}
		if (FldType.ARRAY == ft) {
			Array2 a = getArray2();
			//if (a == null) fail("null array: " + getFld().getName());
			if (a == null) return new Node(FldType.ARRAY.getLabel());
			return a.toXml();
		}
		return new Node("null");
	}
	
	public Object toVo(int idx) {
		FldType ft = getType();
		if (FldType.STR == ft) return getValue();
		if (FldType.NUM == ft) return parseNumber(getValue());
		if (FldType.BOOL == ft) return parseBoolSafe(getValue());
		if (FldType.TS == ft) return parseTs(getValue()); // TODO: ts
		if (FldType.OBJ == ft) return (getObj() == null)?null:getObj().toVo(idx);
		if (FldType.ARRAY == ft) {
			Array2 a = getArray2();
			if (a == null) return new ArrayList();
			return a.toVo(idx);
		}
		return null;
	}
	
	public Object toVo(EvalCtx ctx) {
		if (ctx == null) return null;
		if (ctx.model() == null) return null;
		
		FldType ft = getType();
		String call = null;
		String p[] = null;
		if (getFld() != null) {
			call = getFld().getCall();
			p = getFld().params();
		}
		if (call == null) {
			if (FldType.OBJ == ft) return (getObj() == null)?null:getObj().toVo(ctx);
			if (FldType.STR == ft) return ctx.model();
			if (FldType.NUM == ft) return ctx.model();
			if (FldType.BOOL == ft) return ctx.model();
			if (FldType.TS == ft) return ctx.model();
			return null;
		}
		
		if (FldType.STR == ft) return ctx.call(call,p);
		if (FldType.NUM == ft) return ctx.call(call,p);
		if (FldType.TS == ft) return ctx.call(call,p);
		if (FldType.BOOL == ft) return ctx.call(call,p);
		if (FldType.OBJ == ft) {
			if (getObj() == null) return null;
			if (ctx.pushCall(call,p) == null) return null;
			Object res = getObj().toVo(ctx);
			ctx.pop();
			return res;
		}
		
		if (FldType.ARRAY == ft) {
			Array2 a = getArray2();
			if (a == null) return new ArrayList();
			if (ctx.pushCall(call,p) == null) return null;
			Object res = a.toVo(ctx);
			ctx.pop();
			return res;
		}
		return null;
	}
	

	private Time parseTs(String ts) {
		Time t = null;
		t = Time.parseJson(ts);
		if (t == null) t = Time.parseW3c(ts);
		return t;
	}	
	private Number parseNumber(String n) {
		Number num = null;
		try { num = Integer.parseInt(n); } catch (Exception e) {}
		if (num == null) try { num = Double.parseDouble(n); } catch (Exception e) {}
		return num;
	}
	public Value setNumber(String n) {
		Number num = parseNumber(n);
		return setNumber(num);
	}
	public Value setNumber(Number num) {
		assertType(FldType.NUM);
		setValue(nn(num,"0"));
		return this;
	}
	public Value setString(String str) {
		assertType(FldType.STR);
		setValue(str);
		return this;
	}
	public Value setTs(String ts) {
		assertType(FldType.TS);
		setValue(ts); // TODO: time
		return this;
	}
	public Value setO(Obj o) {
		assertType(FldType.OBJ);
		setObj(o);
		return this;
	}
	public Value setArr(Array2 a) {
		if (a == null) print("null array: " + getFld().getName());
		assertType(FldType.ARRAY);
		setArray2(a);
		return this;
	}
	public Value setBool(boolean b) {
		assertType(FldType.BOOL);
		setValue(""+b);
		return this;
	}
	public Value setBool(String str) {
		return setBool(parseBoolSafe(str));
	}

	public void assertType(FldType ft) {
		Fld fld = getFld();
		if (fld != null) {
			if (fld.getType() != null) {
				if (ft != getType()) {
					fail("wrong type " + ft + " for field: " + fld.getName());
				} 
			} else {
				fld.setType(ft);
			}
		}
		setType(ft);
	}

	public void parse(Ctx ctx, Node n) {
		FldType ft = d().getFldType(n.name());
		if (ft == FldType.STR) setString(n.str());
		if (ft == FldType.NUM) setNumber(n.str());
		if (ft == FldType.TS) setTs(n.str());
		if (ft == FldType.BOOL) setBool(n.str());
		if (ft == FldType.OBJ) setO(d().createObj(ctx,n));
		if (ft == FldType.ARRAY) setArr(d().createArray(ctx,n));
	}
	
	public void onDelete() {
		if (getObj() != null) getObj().delete();
		if (getArray2() != null) getArray2().delete();
	}
	
}